/**
 * FileName: GsonUtil
 *
 * @author: 阿张
 * Date:     2021/3/3 19:09
 */
package com.qx.util;

import com.google.gson.*;
import com.google.gson.annotations.SerializedName;
import com.google.gson.annotations.Since;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

public class GsonUtil {

    protected final static Log log = LogFactory.getLog(GsonUtil.class);

    /** 空的数据 "{}"。 */
    public static final String EMPTY_JSON = "{}";
    /** 空的数组(集合)数据 - {"[]"}。 */
    public static final String EMPTY_JSON_ARRAY = "[]";
    /** 默认的日期/时间字段的格式化模式。 */
    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";

    public GsonUtil(){
        super();
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target						要转换的对象
     * @param targetType					对象的类型
     * @param isSerializeNulls				是否转换空对象(如 : String a = null; isSerializeNulls==true 则转换为 {a:null} ，否则转换为{})
     * @param version						配合@Since(Double)注解使用
     * 										（如：@Since(2.0) String a = "x";@Since(1.0) String b = "y";version==null 则注解不生效；
     * 										如version!=null,则只有@Since<=version的字段才会被转换,如version>=2.0，则转换为{a:"x",b:"y"},version=1.0，则转换为{b:"y"}）
     * @param datePattern					设置Date时间格式转换为json数据的格式，默认"yyyy-MM-dd HH:mm:ss";
     * @param excludesFieldsWithoutExpose	是否只转换@Expose注解的字段，默认false；（如果excludesFieldsWithoutExpose=true，则只有带@Expose注解的字段才会被转换）
     * @param adaptTimestamp				使用Timestamp时间适配器转换Timestamp格式为json字符串
     * @return
     */
    public static String toJson(Object target, Type targetType, boolean isSerializeNulls, Double version,
                                String datePattern, boolean excludesFieldsWithoutExpose,boolean adaptTimestamp) {
        if (target == null) {
            return EMPTY_JSON;
        }
        GsonBuilder builder = new GsonBuilder();
        if (isSerializeNulls) {
            builder.serializeNulls();
        }
        if (version != null) {
            builder.setVersion(version.doubleValue());
        }
        if (!StringUtils.hasText(datePattern)) {
            datePattern = DEFAULT_DATE_PATTERN;
        }
        if(adaptTimestamp){
            TimestampTypeAdapter tt=new TimestampTypeAdapter(datePattern);
            builder.registerTypeAdapter(Timestamp.class, tt);
        }else {
            builder.setDateFormat(datePattern);
        }
        if (excludesFieldsWithoutExpose) {
            builder.excludeFieldsWithoutExposeAnnotation();
        }
        return toJson(target, targetType, builder);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target	要转换的对象
     * @return
     */
    public static String toJson(Object target) {
        return toJson(target, null, false, null, null, false, true);
    }

    /**
     * 将target对象转换为json格式字符串（包括空值字段）
     * @param target	要转换的对象
     * @return
     */
    public static String toJsonWithNull(Object target) {
        return toJson(target, null, true, null, null, false, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target		要转换的对象
     * @param datePattern	设置Date时间格式转换为json数据的格式，默认"yyyy-MM-dd HH:mm:ss";
     * @return
     */
    public static String toJson(Object target, String datePattern) {
        return toJson(target, null, false, null, datePattern, false, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target			要转换的对象
     * @param datePattern		设置Date时间格式转换为json数据的格式，默认"yyyy-MM-dd HH:mm:ss";
     * @param adaptTimestamp	使用Timestamp时间适配器转换Timestamp格式为json字符串
     * @return
     */
    public static String toJson(Object target, String datePattern,boolean adaptTimestamp) {
        return toJson(target, null, false, null, datePattern, false, adaptTimestamp);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target	要转换的对象
     * @param version	配合@Since(Double)注解使用
     * 					（如：@Since(2.0) String a = "x";@Since(1.0) String b = "y";version==null 则注解不生效；
     * @return
     */
    public static String toJson(Object target, Double version) {
        return toJson(target, null, false, version, null, false, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target						要转换的对象
     * @param excludesFieldsWithoutExpose	是否只转换@Expose注解的字段，默认false；（如果excludesFieldsWithoutExpose=true，则只有带@Expose注解的字段才会被转换）
     * @return
     */
    public static String toJson(Object target, boolean excludesFieldsWithoutExpose) {
        return toJson(target, null, false, null, null, excludesFieldsWithoutExpose, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target						要转换的对象
     * @param version						配合@Since(Double)注解使用
     * 										（如：@Since(2.0) String a = "x";@Since(1.0) String b = "y";version==null 则注解不生效；
     * @param excludesFieldsWithoutExpose	是否只转换@Expose注解的字段，默认false；（如果excludesFieldsWithoutExpose=true，则只有带@Expose注解的字段才会被转换）
     * @return
     */
    public static String toJson(Object target, Double version, boolean excludesFieldsWithoutExpose) {
        return toJson(target, null, false, version, null, excludesFieldsWithoutExpose, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target		要转换的对象
     * @param targetType	对象的类型
     * @return
     */
    public static String toJson(Object target, Type targetType) {
        return toJson(target, targetType, false, null, null, false, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target		要转换的对象
     * @param targetType	对象的类型
     * @param version		配合@Since(Double)注解使用
     * 						（如：@Since(2.0) String a = "x";@Since(1.0) String b = "y";version==null 则注解不生效；
     * @return
     */
    public static String toJson(Object target, Type targetType, Double version) {
        return toJson(target, targetType, false, version, null, false, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target						要转换的对象
     * @param targetType					对象的类型
     * @param excludesFieldsWithoutExpose	是否只转换@Expose注解的字段，默认false；（如果excludesFieldsWithoutExpose=true，则只有带@Expose注解的字段才会被转换）
     * @return
     */
    public static String toJson(Object target, Type targetType, boolean excludesFieldsWithoutExpose) {
        return toJson(target, targetType, false, null, null, excludesFieldsWithoutExpose, true);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target						要转换的对象
     * @param targetType					对象的类型
     * @param version						配合@Since(Double)注解使用
     * 										（如：@Since(2.0) String a = "x";@Since(1.0) String b = "y";version==null 则注解不生效；
     * @param excludesFieldsWithoutExpose	是否只转换@Expose注解的字段，默认false；（如果excludesFieldsWithoutExpose=true，则只有带@Expose注解的字段才会被转换）
     * @return
     */
    public static String toJson(Object target, Type targetType, Double version, boolean excludesFieldsWithoutExpose) {
        return toJson(target, targetType, false, version, null, excludesFieldsWithoutExpose, true);
    }

    /**
     * 将json字符串转换为<T>对象
     * @param json			json格式字符串
     * @param token			T类型封装器
     * @param datePattern	设置json数据的格式转换为Date时间格式，默认"yyyy-MM-dd HH:mm:ss";
     * @return
     */
    public static <T> T fromJson(String json, TypeToken<T> token, String datePattern) {
        if (!StringUtils.hasText(json)) {
            return null;
        }
        GsonBuilder builder = new GsonBuilder();
        if (!StringUtils.hasText(datePattern)) {
            datePattern = DEFAULT_DATE_PATTERN;
        }
        Gson gson = builder.create();
        try {
            return (T)gson.fromJson(json, token.getType());
        } catch (Exception ex) {
            log.error(json + " 无法转换为 " + token.getRawType().getName() + " 对象!", ex);
            return null;
        }
    }

    /**
     * 将json字符串转换为<T>对象
     * @param json	json格式字符串
     * @param token	T类型封装器
     * @return
     */
    public static <T> T fromJson(String json, TypeToken<T> token) {
        return fromJson(json, token, null);
    }

    /**
     * 将json字符串转换为<T>对象
     * @param json	json格式字符串
     * @param type	T类型封装器
     * @return
     */
    public static <T> T fromJson(String json, Type type) {
        Gson gson = new Gson();
        return  (T)gson.fromJson(json, (Type) type);
    }
    /**
     * 将json字符串转换为clazz类型对象
     * @param json			json格式字符串
     * @param clazz			转换后的目标类型
     * @param datePattern	设置json数据的格式转换为Date时间格式，默认"yyyy-MM-dd HH:mm:ss";
     * @return
     */
    public static <T> T fromJson(String json, Class<T> clazz, String datePattern) {
        if (!StringUtils.hasText(json)) {
            return null;
        }
        GsonBuilder builder = new GsonBuilder();
        if (!StringUtils.hasText(datePattern)) {
            datePattern = DEFAULT_DATE_PATTERN;
        }
        Gson gson = builder.create();
        try {
            return gson.fromJson(json, clazz);
        } catch (Exception ex) {
            log.error(json + " 无法转换为 " + clazz.getName() + " 对象!", ex);
            return null;
        }
    }

    /**
     * 将json字符串转换为clazz类型对象
     * @param json	json格式字符串
     * @param clazz	转换后的目标类型
     * @return
     */
    public static <T> T fromJson(String json, Class<T> clazz) {
        return fromJson(json, clazz, null);
    }

    /**
     * 将target对象转换为json格式字符串
     * @param target		要转换的对象
     * @param targetType	对象的类型
     * @param builder		Gson实例构造器
     * @return
     */
    public static String toJson(Object target, Type targetType, GsonBuilder builder) {
        if (target == null) return EMPTY_JSON;
        Gson gson = null;
        if (builder == null) {
            gson = new Gson();
        } else {
            gson = builder.create();
        }
        String result = EMPTY_JSON;
        try {
            if (targetType == null) {
                result = gson.toJson(target);
            } else {
                result = gson.toJson(target, targetType);
            }
        } catch (Exception ex) {
            log.warn("目标对象 " + target.getClass().getName() + " 转换 JSON 字符串时，发生异常！", ex);
            if (target instanceof Collection<?> || target instanceof Iterator<?> || target instanceof Enumeration<?>
                    || target.getClass().isArray()) {
                result = EMPTY_JSON_ARRAY;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Properties tp = new Properties();
        tp.put("a", "aa");
        tp.put("b", "bb");
        String json = GsonUtil.toJson(tp.entrySet());
        System.out.println(json);
    }
}

class SomeObject {
    @SerializedName("custom_naming")
    private final String someField;
    @Since(1.0)
    private final String someOtherField;
    @Since(2.0)
    private final int intFiled;

    public SomeObject(String a, String b, int c) {
        this.someField = a;
        this.someOtherField = b;
        this.intFiled = c;
    }
}

/**
 * timestamp时间类型适配器
 * @author lizhiwei
 *
 */
class TimestampTypeAdapter implements JsonSerializer<Timestamp>, JsonDeserializer<Timestamp>{
    private DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public TimestampTypeAdapter(String pattern){
        format=new SimpleDateFormat(pattern);
    }
    public TimestampTypeAdapter(){}

    @Override
    public JsonElement serialize(Timestamp src, Type arg1, JsonSerializationContext arg2) {
        if(src==null)return null;
        String dateFormatAsString = format.format(new Date(src.getTime()));
        return new JsonPrimitive(dateFormatAsString);
    }

    @Override
    public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if(json==null)return null;
        if (!(json instanceof JsonPrimitive)) {
            throw new JsonParseException("数据有误！");
        }
        try {
            Date date = format.parse(json.getAsString());
            return new Timestamp(date.getTime());
        } catch (java.text.ParseException e) {
            // TODO Auto-generated catch block
            GsonUtil.log.error(e.getMessage(), e);
            return null;
        }
    }
}
